﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using Net;
using UnityEditor;
using UnityEngine;

namespace Editor.Tools.Proto
{

    class Token
    {
        public const string MESSAGE = "message";
        public const string ENUM = "enum";
        
        public const string REPEATED = "repeated";
        public const string OPTIONAL = "optional";
        public const string REQUIRED = "required";
        
        
        public const string INT32 = "int32";
        public const string INT64 = "int64";
        public const string UINT32 = "uint32";
        public const string UINT64 = "uint64";
        public const string SINT32 = "sint32";
        public const string SINT64 = "sint64";
        public const string FIXED32 = "fixed32";
        public const string FIXED64 = "fixed64";
        public const string SFIXED32 = "sfixed32";
        public const string SFIXED64 = "sfixed64";
        public const string FLOAT = "float";
        public const string DOUBLE = "double";
        public const string BOOL = "bool";
        public const string STRING = "string";
        public const string BYTES = "bytes";
        
        // 分号
        public const string SEMICOLON = ";";
        // 等号
        public const string EQUAL = "=";
        // 左大括号
        public const string LEFT_BRACE = "{";
        // 右大括号
        public const string RIGHT_BRACE = "}";
        
    }

    class FieldInfo
    {
        public string decorator;
        public string type;
        public string name;
        public int number;

        public string comment;
        
        
    }
    
    class EnumInfo
    {
        public string comment;
        public string enumName;
        public Dictionary<string, int> enumValues = new Dictionary<string, int>();
    }
    
    class MessageInfo
    {
        public string messageName;
        public List<FieldInfo> fields = new List<FieldInfo>();
        public int messageId;

        public string comment;
    }
    
    class ProcessContext
    {
        public Stack<ProcessState> stack = new();
        public ProcessState currentState;
        public string lastComment;
        public int lineCounter;
        public string filePath;
        
        public List<MessageInfo> protoInfos = new();
        public List<EnumInfo> enumInfos = new();
        public HashSet<string> enumSet = new();
        
        public string GetComment()
        {
            string comment = lastComment;
            lastComment = string.Empty;
            return comment;
        }

        public void PushState(ProcessState newState)
        {
            stack.Push(currentState);
            currentState = newState;
            newState.context = this;
            newState.OnEnter();
        }
        
        public void PopState()
        {
            currentState.OnExit();
            currentState.context = null;
            currentState = stack.Pop();
            
            if (currentState == null)
            {
                throw new Exception("栈为空");
            }
        }
    }

    abstract class ProcessState
    {
        public ProcessContext context;
        public abstract void Process(string token);
        public abstract void OnEnter();
        public abstract void OnExit();
    }
    
    // 消息体外层内容过滤状态
    class MiscFilterState : ProcessState
    {
        public override void Process(string token)
        {
            switch (token)
            {
                case Token.MESSAGE:
                {
                    var newState = new ProcessMessageBodyState();
                    context.PushState(newState);
                    break;
                }
                    
                case Token.ENUM:
                {
                    var newState = new ProcessEnumBodyState();
                    context.PushState(newState);
                    break;
                }
            }
        }

        public override void OnEnter()
        {
        }

        public override void OnExit()
        {
        }
    }
    
    class ProcessMessageBodyState : ProcessState
    {
        private MessageInfo messageInfo = new MessageInfo();
        private bool inBody = false;
        public override void Process(string token)
        {
            switch (token)
            {
                case Token.LEFT_BRACE:
                {
                    inBody = true;
                    break;
                }
                
                case Token.RIGHT_BRACE:
                {
                    context.PopState();
                    break;
                }
                
                case Token.MESSAGE:
                {
                    var newState = new ProcessMessageBodyState();
                    context.PushState(newState);
                    break;
                }
                
                case Token.ENUM:
                {
                    var newState = new ProcessEnumBodyState();
                    context.PushState(newState);
                    break;
                }
                
                default:
                {
                    if (inBody)
                    {
                        var newState = new ProcessMessageFieldState();
                        newState.targetMessageInfo = messageInfo;
                        context.PushState(newState);
                        context.currentState.Process(token);
                    }
                    else
                    {
                        messageInfo.messageName = token;
                    }
                    break;
                }
            }
        }

        public override void OnEnter()
        {
            messageInfo.comment = context.GetComment();
        }

        public override void OnExit()
        {
            context.protoInfos.Add(messageInfo);
        }
    }
    
    class ProcessMessageFieldState : ProcessState
    {
        public MessageInfo targetMessageInfo;
        public FieldInfo fieldInfo = new ();
        public override void Process(string token)
        {
            switch (token)
            {
                case Token.SEMICOLON:
                {
                    context.PopState();
                    break;
                }
                
                case Token.EQUAL:
                {
                    break;
                }
                
                case Token.REPEATED:
                case Token.OPTIONAL:
                case Token.REQUIRED:
                {
                    fieldInfo.decorator = token;
                    break;
                }
                
                default:
                {
                    if (fieldInfo.type == null)
                    {
                        fieldInfo.type = token;
                    }
                    else if (fieldInfo.name == null)
                    {
                        fieldInfo.name = token;
                    }
                    else if (fieldInfo.number == 0)
                    {
                        try
                        {
                            fieldInfo.number = int.Parse(token);
                        }
                        catch (FormatException e)
                        {
                            Debug.LogException(new Exception($"expected number but got string: {token} at {context.filePath} line {context.lineCounter}"));
                        }
                        catch (Exception e)
                        {
                            Debug.LogException(e);
                            throw;
                        }
                    }
                    break;
                }
            }
        }

        public override void OnEnter()
        {
            fieldInfo.comment = context.GetComment();
        }

        public override void OnExit()
        {
            targetMessageInfo.fields.Add(fieldInfo);
        }
    }
    
    class ProcessEnumBodyState : ProcessState
    {
        private EnumInfo enumInfo = new EnumInfo();
        private bool inBody = false;

        public override void Process(string token)
        {
            switch (token)
            {
                case Token.LEFT_BRACE:
                {
                    inBody = true;
                    break;
                }
                
                case Token.RIGHT_BRACE:
                {
                    context.PopState();
                    break;
                }
                
                case Token.ENUM:
                {
                    var newState = new ProcessEnumBodyState();
                    context.PushState(newState);
                    break;
                }
                
                case Token.MESSAGE:
                {
                    var newState = new ProcessMessageBodyState();
                    context.PushState(newState);
                    break;
                }
                
                default:
                {
                    if (inBody)
                    {
                        var newState = new ProcessEnumFieldState();
                        newState.targetEnumInfo = enumInfo;
                        context.PushState(newState);
                        context.currentState.Process(token);
                    }
                    else
                    {
                        enumInfo.enumName = token;
                    }
                    break;
                }
            }
        }

        public override void OnEnter()
        {
            enumInfo.comment = context.GetComment();
        }

        public override void OnExit()
        {
            context.enumInfos.Add(enumInfo);
            context.enumSet.Add(enumInfo.enumName);
        }
    }

    class ProcessEnumFieldState : ProcessState
    {
        public EnumInfo targetEnumInfo;
        public string enumValueName;
        public int enumValue;
        public override void Process(string token)
        {
            switch (token)
            {
                case Token.SEMICOLON:
                {
                    context.PopState();
                    break;
                }
                
                case Token.EQUAL:
                {
                    break;
                }
                
                default:
                {
                    if (enumValueName == null)
                    {
                        enumValueName = token;
                    }
                    else if (enumValue == 0)
                    {
                        enumValue = int.Parse(token);
                    }
                    break;
                }
            }
        }

        public override void OnEnter()
        {
            targetEnumInfo.comment = context.GetComment();
        }

        public override void OnExit()
        {
            targetEnumInfo.enumValues.Add(enumValueName, enumValue);
        }
        
    }

    public static class Proto2CS
    {
        
        // 分隔 Token
        private static Regex TokenRegex = new Regex(@"(\w+|{|}|;|=)"); 
        
        // 消息id
        private const string messageIdPattern = @"\s*//\s*id\s*=\s*(?<messageId>\d+)";
        private static readonly Regex messageIdRegex = new Regex(messageIdPattern);
        
        
        [MenuItem("Tools/Net/Proto2CS")]
        public static void Process()
        {
            string rootDirectory = Regex.Replace(Application.dataPath, "Client/.*", "proto");
            // string rootDirectory = Application.dataPath + "/Msg";
            if (!Directory.Exists(rootDirectory))
            {
                Debug.LogError("proto Root Directory Not Exist!");
                return;
            }
            
            // MessageDefine.cs
            StringBuilder messageDefine = new StringBuilder();
            messageDefine.AppendLine("namespace Net");
            messageDefine.AppendLine("{");
            messageDefine.AppendLine("    public class MessageDefine");
            messageDefine.AppendLine("    {");
            
            // MessageFactoryRegistry.cs
            StringBuilder messageFactoryRegistry = new StringBuilder();
            messageFactoryRegistry.AppendLine("using System;");
            messageFactoryRegistry.AppendLine("using System.Collections.Generic;");
            messageFactoryRegistry.AppendLine();
            messageFactoryRegistry.AppendLine("namespace Net");
            messageFactoryRegistry.AppendLine("{");
            messageFactoryRegistry.AppendLine("    public class MessageFactoryRegistry");
            messageFactoryRegistry.AppendLine("    {");
            messageFactoryRegistry.AppendLine("        public static readonly Dictionary<short, Func<BaseMessage>> MessageCreators = new()");
            messageFactoryRegistry.AppendLine("        {");
            
            
            // cs对应的根文件夹
            string csRootDirectory = Application.dataPath + "/Scripts/Net/Msg";
            
            // 遍历每个文件及其子文件夹 如果有proto文件则生成对应的cs文件
            string[] files = Directory.GetFiles(rootDirectory, "*.proto", SearchOption.AllDirectories);
            foreach (string file in files)
            {
                // 如果文件夹不存在则创建
                string csDirectory = file.Replace(rootDirectory, csRootDirectory);
                csDirectory = csDirectory.Replace(Path.GetFileName(csDirectory), "");
                if (!Directory.Exists(csDirectory))
                {
                    Directory.CreateDirectory(csDirectory);
                }
                
                
                // 生成cs文件
                ProcessContext context = ParseProto(file);
                string csContent = GenerateCS(context, messageDefine, messageFactoryRegistry);
                string csFilePath = file.Replace(rootDirectory, csRootDirectory).Replace(".proto", ".cs");
                if (!File.Exists(csFilePath))
                {
                    File.Create(csFilePath).Close();
                }
                File.WriteAllText(csFilePath, csContent);
            }
            
            // MessageDefine.cs
            messageDefine.AppendLine("    }");
            messageDefine.AppendLine("}");
            string messageDefineFilePath = csRootDirectory + "/MessageDefine.cs";
            if (!File.Exists(messageDefineFilePath))
            {
                File.Create(messageDefineFilePath).Close();
            }
            File.WriteAllText(messageDefineFilePath, messageDefine.ToString());
            
            // MessageFactoryRegistry.cs
            messageFactoryRegistry.AppendLine("        };");
            messageFactoryRegistry.AppendLine("    }");
            messageFactoryRegistry.AppendLine("}");
            string messageFactoryRegistryFilePath = csRootDirectory + "/MessageFactoryRegistry.cs";
            if (!File.Exists(messageFactoryRegistryFilePath))
            {
                File.Create(messageFactoryRegistryFilePath).Close();
            }
            File.WriteAllText(messageFactoryRegistryFilePath, messageFactoryRegistry.ToString());
        }

        private static ProcessContext ParseProto(string filePath)
        {
            using StreamReader reader = new StreamReader(filePath);
            
            ProcessContext context = new ProcessContext
            {
                lineCounter = 0,
                lastComment = string.Empty,
                filePath = filePath,
            };
            var state = new MiscFilterState();
            context.PushState(state);

            string line;
            while ((line = reader.ReadLine()) != null)
            {
                ++context.lineCounter;
                // 忽略被注释掉的内容
                if (line.Trim().StartsWith("//"))
                {
                    context.lastComment = line;
                    continue;
                }
                
                int commentIndex = line.IndexOf("//", StringComparison.Ordinal);
                if (commentIndex != -1)
                {
                    context.lastComment = line.Substring(commentIndex);
                    line = line.Substring(0, commentIndex);
                }
                
                MatchCollection matches = TokenRegex.Matches(line);
                foreach (Match match in matches)
                {
                    string token = match.Value;
                    // Debug.Log(token);
                    context.currentState.Process(token);
                }
            }

            // PrintContext(context);

            return context;
        }
        
        private static string GenerateCS(ProcessContext context, StringBuilder messageDefine, StringBuilder messageFactoryRegistry)
        {
            StringBuilder csContent = new StringBuilder();
            csContent.AppendLine("using Net;");
            csContent.AppendLine("using System.Collections.Generic;");
            csContent.AppendLine("using System.IO;");
            csContent.AppendLine("");

            // 枚举
            foreach (var enumInfo in context.enumInfos)
            {
                string enumName = enumInfo.enumName;
                if (!string.IsNullOrEmpty(enumInfo.comment))
                {
                    csContent.AppendLine("/// <summary>");
                    csContent.AppendLine($"/// {enumInfo.comment}");
                    csContent.AppendLine("/// </summary>");
                }
                csContent.AppendLine($"public enum {enumName}");
                csContent.AppendLine("{");
                foreach (var (key, value) in enumInfo.enumValues)
                {
                    csContent.AppendLine($"    {key} = {value},");
                }
                csContent.AppendLine("}\n");
                
                
            }
            
            // 消息体
            foreach (MessageInfo messageInfo in context.protoInfos)
            {
                string messageName = messageInfo.messageName;
                
                bool isMessage = IsMessage(messageName);
                if (!string.IsNullOrEmpty(messageInfo.comment))
                {
                    csContent.AppendLine("/// <summary>");
                    csContent.AppendLine($"/// {messageInfo.comment}");
                    csContent.AppendLine("/// </summary>");
                }

                string indent = "";
                csContent.AppendLine($"public class {messageName} : { (isMessage ? nameof(BaseMessage) : nameof(IMessageSerializer))}");
                csContent.AppendLine("{");
                indent += "    ";
                
                // 生成ID以及MessageDefine
                if (isMessage)
                {
                    csContent.AppendLine(indent + $"public override short MsgId => MessageDefine.{messageName};");
                    GenerateMessageDefine(messageDefine, messageInfo);
                    GenerateMessageFactoryRegistry(messageFactoryRegistry, messageInfo);
                }


                // 字段
                foreach (FieldInfo fieldInfo in messageInfo.fields)
                {
                    string decorate = fieldInfo.decorator;
                    string fieldType = fieldInfo.type;
                    string fieldName = fieldInfo.name;
                    string fieldDesc = fieldInfo.comment;
                    csContent.AppendLine(indent + $"public {ProtoType2CsType(decorate, fieldType)} {fieldName};{(string.IsNullOrEmpty(fieldDesc) ? string.Empty : ( fieldDesc))}");
                    if (IsBaseType(fieldType))
                    {
                        csContent.AppendLine(indent + $"private readonly uint tag_{fieldName} = ProtoHelper.CalcTag({fieldInfo.number}, \"{fieldType}\");");
                    }
                    else if (IsEnum(fieldType, context))
                    {
                        csContent.AppendLine(indent + $"private readonly uint tag_{fieldName} = ProtoHelper.CalcTag({fieldInfo.number}, WireType.Varint);");
                    }
                    else
                    {
                        csContent.AppendLine(indent + $"private readonly uint tag_{fieldName} = ProtoHelper.CalcTag({fieldInfo.number}, WireType.LengthDelimited);");
                    }
                    // Debug.Log($"messageName:{messageName}\tdecorate: {decorate}\tfieldType: {fieldType}\tfieldName: {fieldName}\tfieldDesc: {fieldDesc}");
                }
                csContent.AppendLine("");

                // 生成Deserialize 每个字段的从内存流读取方法
                csContent.AppendLine(indent + $"public{(isMessage ? " override" : string.Empty)} void Deserialize(byte[] data,ref int offset)");
                csContent.AppendLine(indent + "{");
                indent += "    ";
                csContent.AppendLine(indent + "int newOffset = offset;");
                foreach (FieldInfo fieldInfo in messageInfo.fields)
                {
                    string decorate = fieldInfo.decorator;
                    string fieldType = fieldInfo.type;
                    string fieldName = fieldInfo.name;
                    // int fieldNumber = fieldInfo.number;
                    string csType = ProtoType2CsType(decorate, fieldType);
                    string baseCsType = csType.Replace("[]", string.Empty);

                    // 如果是repeated
                    if ("repeated".Equals(decorate))
                    {
                        StringBuilder sb = new StringBuilder();
                        sb.AppendLine(indent + $"newOffset = ProtoHelper.ReadRawTagInPlace(data, offset, out uint tag_read_{fieldName});");
                        sb.AppendLine(indent + $"if (tag_{fieldName} == tag_read_{fieldName})");
                        sb.AppendLine(indent + "{");
                        indent += "    ";
                        sb.AppendLine(indent + $"offset = newOffset;");
                        sb.AppendLine(indent + $"int tag_{fieldName}_elem, tag__{fieldName}_elem = 0;");
                        sb.AppendLine(indent + $"int offsetBeforeReadTag_{fieldName} = offset;");
                        sb.AppendLine(indent + $"ProtoHelper.ReadTag(data, ref offset, out tag_{fieldName}_elem, out int _);");
                        sb.AppendLine(indent + $"List<{baseCsType}> list_{fieldName} = new List<{baseCsType}>();");
                        sb.AppendLine(indent + "do");
                        sb.AppendLine(indent + "{");
                        indent += "    ";
                        sb.AppendLine(indent + $"int byte_len_{fieldName} = ProtoHelper.ReadLength(data, ref offset);");
                        sb.AppendLine(indent + $"int end_{fieldName} = offset + byte_len_{fieldName};");
                        sb.AppendLine(indent + $"while ( offset < end_{fieldName} )");
                        sb.AppendLine(indent + "{");
                        indent += "    ";
                        if (IsBaseType(fieldType))
                        {
                            sb.AppendLine(indent + $"list_{fieldName}.Add(ProtoHelper.{ProtoHelper.GetReadFuncName(fieldType)}(data, ref offset));");
                        }
                        else if (IsEnum(fieldType, context))
                        {
                            sb.AppendLine(indent + $"list_{fieldName}.Add(({baseCsType})ProtoHelper.ReadInt32(data, ref offset));");
                        }
                        else
                        {
                            sb.AppendLine(indent + $"offsetBeforeReadTag_{fieldName} = offset;");
                            sb.AppendLine(indent + $"ProtoHelper.ReadTag(data, ref offset, out tag_{fieldName}_elem, out int _);");
                            sb.AppendLine(indent + $"offset = offsetBeforeReadTag_{fieldName};");
                            sb.AppendLine(indent + $"{baseCsType} {baseCsType.ToLower()} = new {baseCsType}();");
                            sb.AppendLine(indent + $"{baseCsType.ToLower()}.Deserialize(data,ref offset);");
                            sb.AppendLine(indent + $"list_{fieldName}.Add({baseCsType.ToLower()});");
                        }
                        indent = indent.Substring(4);
                        sb.AppendLine(indent + "}");
                        sb.AppendLine(indent + $"if (offset >= data.Length) break;");
                        sb.AppendLine(indent + $"offsetBeforeReadTag_{fieldName} = offset;");
                        sb.AppendLine(indent + $"ProtoHelper.ReadTag(data, ref offset, out tag__{fieldName}_elem, out int _);");
                        indent = indent.Substring(4);
                        sb.AppendLine(indent + $"}} while (tag_{fieldName}_elem == tag__{fieldName}_elem);");
                        sb.AppendLine(indent + $"if(offset < data.Length) offset = offsetBeforeReadTag_{fieldName}; ");
                        sb.AppendLine(indent + $"{fieldName} = list_{fieldName}.ToArray();");
                        indent = indent.Substring(4);
                        sb.AppendLine(indent + "}");
                        csContent.Append(sb.ToString());
                    }
                    else
                    {
                        // 如果是基础类型
                        if (IsBaseType(fieldType))
                        {
                            csContent.AppendLine(indent + $"newOffset = ProtoHelper.ReadRawTagInPlace(data, offset, out uint tag_read_{fieldName});");
                            csContent.AppendLine(indent + $"if (tag_{fieldName} == tag_read_{fieldName})");
                            csContent.AppendLine(indent + "{");
                            indent += "    ";
                            csContent.AppendLine(indent + "offset = newOffset;");
                            csContent.AppendLine(indent + $"{fieldName} = ProtoHelper.{ProtoHelper.GetReadFuncName(fieldType)}(data, ref offset);");
                            indent = indent.Substring(4);
                            csContent.AppendLine(indent + "}");
                        }
                        else if (IsEnum(fieldType, context))
                        {
                            csContent.AppendLine(indent + $"newOffset = ProtoHelper.ReadRawTagInPlace(data, offset, out uint tag_read_{fieldName});");
                            csContent.AppendLine(indent + $"if (tag_{fieldName} == tag_read_{fieldName})");
                            csContent.AppendLine(indent + "{");
                            indent += "    ";
                            csContent.AppendLine(indent + "offset = newOffset;");
                            csContent.AppendLine(indent + $"{fieldName} = ({baseCsType})ProtoHelper.ReadInt32(data, ref offset);");
                            indent = indent.Substring(4);
                            csContent.AppendLine(indent + "}");
                        }
                        else
                        {
                            // 如果是自定义类型
                            csContent.AppendLine(indent + $"{fieldName} = new {csType}();");
                            csContent.AppendLine(indent + $"newOffset = ProtoHelper.ReadRawTagInPlace(data, offset, out uint tag_read_{fieldName});");
                            csContent.AppendLine(indent + $"if (tag_{fieldName} == tag_read_{fieldName})");
                            csContent.AppendLine(indent + "{");
                            indent += "    ";
                            csContent.AppendLine(indent + "offset = newOffset;");
                            csContent.AppendLine(indent + $"ProtoHelper.ReadLength(data, ref offset);");
                            csContent.AppendLine(indent + $"{fieldName}.Deserialize(data,ref offset);");
                            indent = indent.Substring(4);
                            csContent.AppendLine(indent + "}");
                        }
                    }
                    csContent.AppendLine();
                }
                indent = indent.Substring(4);
                csContent.AppendLine(indent + "}\n");
                
                // 生成每个字段写入内存流的方法
                csContent.AppendLine(indent + $"public{(isMessage ? " override" : string.Empty)} byte[] Serialize()");
                csContent.AppendLine(indent + "{");
                indent += "    ";
                csContent.AppendLine(indent + "byte[] __bytes;");
                csContent.AppendLine(indent + "using MemoryStream ms = new MemoryStream();");
                csContent.AppendLine(indent + "using BinaryWriter bw = new BinaryWriter(ms);");
                csContent.AppendLine();
                foreach (FieldInfo fieldInfo in messageInfo.fields)
                {
                    string decorate = fieldInfo.decorator;
                    string fieldType = fieldInfo.type;
                    string fieldName = fieldInfo.name;
                    int fieldNumber = fieldInfo.number;
                    // string csType = ProtoType2CsType(decorate, fieldType);
                    // string baseCsType = csType.Replace("[]", string.Empty);

                    // 如果是repeated
                    if ("repeated".Equals(decorate))
                    {
                        StringBuilder sb = new StringBuilder();
                        csContent.AppendLine(indent + $"using MemoryStream ms_{fieldName} = new MemoryStream();");
                        csContent.AppendLine(indent + $"using BinaryWriter bw_{fieldName} = new BinaryWriter(ms_{fieldName});");
                        csContent.AppendLine(indent + $"foreach (var item in {fieldName})");
                        csContent.AppendLine(indent +  "{");
                        indent += "    ";
                        csContent.AppendLine(indent + $"ms_{fieldName}.SetLength(0);");
                        csContent.AppendLine(indent + $"ms_{fieldName}.Seek(0, SeekOrigin.Begin);");
                        if (IsBaseType(fieldType))
                        {
                            csContent.AppendLine(indent + $"__bytes = ProtoHelper.{ProtoHelper.GetWriteFuncName(fieldType)}(item);");
                            if (fieldType == "string" || fieldType == "bytes")
                            {
                                csContent.AppendLine(indent + "if (__bytes != null && __bytes.Length > 0)");
                            }
                            csContent.AppendLine(indent + "{");
                            indent += "    ";
                            csContent.AppendLine(indent + $"bw_{fieldName}.Write(__bytes);");
                            indent = indent.Substring(4);
                            csContent.AppendLine(indent + "}");
                        }
                        else if (IsEnum(fieldType, context))
                        {
                            csContent.AppendLine(indent + $"bw_{fieldName}.Write(ProtoHelper.WriteInt32((int)item));");
                        }
                        else
                        {
                            csContent.AppendLine(indent + "if (item == null) continue;");
                            csContent.AppendLine(indent + $"bw_{fieldName}.Write(item.Serialize());");
                        }
                        
                        csContent.AppendLine(indent + $"var bytes_{fieldName} = ms_{fieldName}.ToArray();");
                        csContent.AppendLine(indent + $"if (bytes_{fieldName}.Length > 0)");
                        csContent.AppendLine(indent + "{");
                        indent += "    ";
                        csContent.AppendLine(indent + $"bw.Write(ProtoHelper.WriteTag({fieldNumber}, WireType.LengthDelimited));");
                        csContent.AppendLine(indent + $"bw.Write(ProtoHelper.WriteInt32(bytes_{fieldName}.Length));");
                        csContent.AppendLine(indent + $"bw.Write(bytes_{fieldName});");
                        indent = indent.Substring(4);
                        csContent.AppendLine(indent + "}");
                        indent = indent.Substring(4);
                        csContent.AppendLine(indent + "}");

                        csContent.Append(sb.ToString());
                    }
                    else
                    {
                        // 如果是基础类型
                        if (IsBaseType(fieldType))
                        {
                            csContent.AppendLine(indent + $"__bytes = ProtoHelper.{ProtoHelper.GetWriteFuncName(fieldType)}({fieldName});");
                            if (fieldType == "string" || fieldType == "bytes")
                            {
                                csContent.AppendLine(indent + "if (__bytes != null && __bytes.Length > 0)");
                            }
                            csContent.AppendLine(indent + "{");
                            indent += "    ";
                            csContent.AppendLine(indent + $"bw.Write(ProtoHelper.WriteTag({fieldNumber}, \"{fieldType}\"));");
                            csContent.AppendLine(indent + $"bw.Write(__bytes);");
                            indent = indent.Substring(4);
                            csContent.AppendLine(indent + "}");
                        }
                        else if (IsEnum(fieldType, context))
                        {
                            csContent.AppendLine(indent + $"bw.Write(ProtoHelper.WriteTag({fieldNumber}, WireType.Varint));");
                            csContent.AppendLine(indent + $"bw.Write(ProtoHelper.WriteInt32((int){fieldName}));");
                        }
                        else
                        {
                            // 如果是自定义类型
                            csContent.AppendLine(indent + $"if ({fieldName} != null)");
                            csContent.AppendLine(indent + "{");
                            indent += "    ";
                            csContent.AppendLine(indent + $"__bytes = {fieldName}.Serialize();");
                            csContent.AppendLine(indent + $"if (__bytes != null && __bytes.Length > 0)");
                            csContent.AppendLine(indent + "{");
                            indent += "    ";
                            csContent.AppendLine(indent + $"bw.Write(ProtoHelper.WriteTag({fieldNumber}, WireType.LengthDelimited));");
                            csContent.AppendLine(indent + $"bw.Write(ProtoHelper.WriteInt32(__bytes.Length));");
                            csContent.AppendLine(indent + $"bw.Write(__bytes);");
                            indent = indent.Substring(4);
                            csContent.AppendLine(indent + "}");
                            indent = indent.Substring(4);
                            csContent.AppendLine(indent + "}");
                        }
                    }
                    csContent.AppendLine();
                    
                }

                csContent.AppendLine(indent + "return ms.ToArray();");
                indent = indent.Substring(4);
                csContent.AppendLine(indent + "}");
                indent = indent.Substring(4);
                csContent.AppendLine(indent + "}\n");
            }

            return csContent.ToString();
        }
        
        private static readonly string[] messageTags = new []{"LS2U", "GS2U", "U2LS", "U2GS", "cmd"};
        private static bool IsMessage(string messageName)
        {
            foreach (string tag in messageTags)
            {
                if (messageName.Contains(tag))
                {
                    return true;
                }
            }

            return false;
        }
        
        private static bool IsEnum(string type, ProcessContext context)
        {
            return context.enumSet.Contains(type);
        }
        
        private static readonly Dictionary<string, string> typeMapping = new Dictionary<string, string>
        {
            { "int32", "int" },
            { "int64", "long" },
            { "uint32", "uint" },
            { "uint64", "ulong" },
            { "sint32", "int" },
            { "sint64", "long" },
            { "fixed32", "uint" },
            { "fixed64", "ulong" },
            { "sfixed32", "int" },
            { "sfixed64", "long" },
            { "bool", "bool" },
            { "string", "string" },
            { "bytes", "byte[]" },
            { "float", "float" },
            { "double", "double" },
            // Add more mappings if needed
        };
        
        private static bool IsBaseType(string fieldType)
        {
            return typeMapping.ContainsKey(fieldType);
        }
        
        private static string ProtoType2CsType(string decorate, string fieldType)
        {
            if (typeMapping.TryGetValue(fieldType, out string csType))
            {
                return "repeated".Equals(decorate) ? $"{csType}[]" : csType;
            }
            else
            {
                // Handle custom types (messages, enums) here if needed
                return "repeated".Equals(decorate) ? $"{fieldType}[]" : fieldType;
            }
        }
        
        private static void GenerateMessageDefine(StringBuilder messageDefine, MessageInfo messageInfo)
        {
            string messageName = messageInfo.messageName;
            Match messageIdMatch = messageIdRegex.Match(messageInfo.comment);
            if (!messageIdMatch.Success)
            {
                Debug.LogError($"Message Without MessageId define: {messageName}");
                return;
            }
            var messageIdGroup = messageIdMatch.Groups["messageId"];
            if (messageIdGroup == null)
            {
                Debug.LogError($"Message Without MessageId define: {messageName}");
                return;
            }
            messageDefine.AppendLine($"        public const short {messageName} = {messageIdGroup.Value};");
        }
        
        private static void GenerateMessageFactoryRegistry(StringBuilder messageFactoryRegistry, MessageInfo messageInfo)
        {
            string messageName = messageInfo.messageName;
            messageFactoryRegistry.AppendLine($"            {{ MessageDefine.{messageName}, () => new {messageName}() }},");
        }
        
        private static void PrintContext(ProcessContext context)
        {
            foreach (var protoInfo in context.protoInfos)    
            {
                Debug.Log($"message: {protoInfo.messageName}");
                foreach (var fieldInfo in protoInfo.fields)
                {
                    Debug.Log($"{fieldInfo.decorator} {fieldInfo.type} {fieldInfo.name} {fieldInfo.number}");
                }
            }
            
            foreach (var enumInfo in context.enumInfos)
            {
                Debug.Log($"enum: {enumInfo.enumName}");
                foreach (var (key, value) in enumInfo.enumValues)
                {
                    Debug.Log($"{key} = {value}");
                }
            }
        }

    }
}